/*
* Copyright 2011 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/
package org.etherframework.core.context
{
	import flash.utils.Dictionary;
	
	import org.etherframework.common.IDestroyable;
	import org.etherframework.common.IDisposable;

	// TODO: Refactor.
	[ExcludeClass]
	public final class Contexts implements IContexts, IDestroyable
	{
		private const _singles:Dictionary = new Dictionary();
		private const _info:Dictionary = new Dictionary();
		
		public function contextual(context:Context, type:Class,
								   creator:IContextualCreator):IContextual
		{
			var contextType:Class = context["constructor"];
			var info:ContextualInfo;
			var child:ContextualInfo;
			
			if (creator is ISingleContextual)
			{
				if (_singles[contextType] && _singles[contextType][type])
				{
					// TODO: Think about improvement.
					// Not a good solution but it is allowable in case of sync. 
					// execution of persistent contextuals.
					(_singles[contextType][type] as IContextual).context.merge(context);
					
					return _singles[contextType][type] as IContextual;
				}
				
				const dict:Dictionary = _singles[contextType] || 
					(_singles[contextType] = new Dictionary());
				
				info = _info[creator] || 
					(_info[creator] = new ContextualInfo(null, type, creator, null));
				
				if (contextType == Default)
					return (dict[type] = creator);
				
				child = new ContextualInfo(info, type, 
					(dict[type] = creator.create(context, type)), null);
				
				if (!info.children)
					info.children = new Vector.<ContextualInfo>();
				info.children.push(child);
				
				return child.contextual;
			}
			else if (creator is IPersistentContextual)
			{
				info = _info[creator] || 
					(_info[creator] = new ContextualInfo(null, type, creator, null));
				
				child = new ContextualInfo(info, type, 
					creator.create(context, type), null);
				
				if (!info.children)
					info.children = new Vector.<ContextualInfo>();
				info.children.push(child);
				
				return child.contextual;
			}
			
			return creator.create(context, type);
		}
		
		public function dispose(contextual:IContextual):void
		{
			if (_info[contextual])
			{
				const info:ContextualInfo = _info[contextual];
				const children:Vector.<ContextualInfo> = info.children;
				
				for each (var item:ContextualInfo in children) 
				{
					dispose(item.contextual);
				}
				
				delete _info[contextual];
				
				if (_singles[contextual.context] && 
					_singles[contextual.context][info.type])
				{
					delete _singles[contextual.context][info.type];
				}
			}
			
			if (contextual is IDisposable)
				IDisposable(contextual).dispose();
		}
		
		public function destroy():void
		{
			const info:Dictionary = _info;
			
			for (var item:Object in info) 
			{
				dispose(item as IContextual);
			}
		}
	}
}